package com.ews.onlineexchange.service.assess;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.enterprise.event.Event;
import javax.enterprise.inject.Model;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;

import org.hibernate.CacheMode;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;

import com.ews.onlineexchange.enumType.RecordStatus;
import com.ews.onlineexchange.model.Attachment;
import com.ews.onlineexchange.model.ServiceSupplier;
import com.ews.onlineexchange.model.assess.AssessRecord;
import com.ews.onlineexchange.model.assess.Dict;
import com.ews.onlineexchange.model.assess.PeriodType;
import com.ews.onlineexchange.service.qualifier.AssessRecodOperationEvent;

@Model
public class AssessRecordService {	
	
	@Inject
	EntityManager entityManager;

	@Inject
	@AssessRecodOperationEvent
	Event<Object[]>				assessRecodOperationEvent;

	public void keep( String id ) {
		assessRecodOperationEvent.fire( new Object[] { id, RecordStatus.Keeped } );
	}
	
	public AssessRecord find( String id ) {
		return entityManager.find( AssessRecord.class, id );
	}
	
	private static final String FIND_BY_TITLE_SQL = "							"
			+ "	SELECT				count(record.id) 							"
			+ "	FROM				AssessRecord record							"
			+ "	WHERE 				record.delFlag = '0'						"
			+ "	AND					record.dict.id = :dictId 					"
			+ " AND 				record.serviceSupplier.id = :supplierId		";
	public boolean findByDictIdAndSupplierId(String dictId, String supplierId) {
		Query query = entityManager.createQuery(FIND_BY_TITLE_SQL);
		query.setParameter("dictId", dictId);
		query.setParameter("supplierId", supplierId);
		long count = (long)query.getSingleResult();
		if(count > 0) return true;
		else return false;
	}
	
	@Transactional
	public void merge(AssessRecord record) {
		entityManager.merge(record);
	}
	
	/*
	 * 根据当前时间，自动判断已经过期的周期性得分项，并还原中介机构的分数
	 */
	public void rollback() {
		CriteriaBuilder cb = entityManager.getCriteriaBuilder();
		CriteriaQuery<AssessRecord> criteria = cb.createQuery( AssessRecord.class );        
        Root<AssessRecord> entity = criteria.from( AssessRecord.class );
        
        List<Predicate> restrictions = new ArrayList<>();
        
		restrictions.add( cb.equal( entity.get( "recordStatus" ), RecordStatus.Keeped ) );
		restrictions.add( cb.isFalse( entity.get( "rolledback" ) ) );
		
		Calendar cal = Calendar.getInstance();		
		
		Predicate yearPeriodRestriction = cb.and( 
				cb.equal( entity.join( "dict" ).get( "periodType" ), PeriodType.Year ),
				cb.lessThan( entity.get( "periodYear" ), cal.get( Calendar.YEAR ) - 1 )
		);
		
		Predicate seasonPeriodRestriction = cb.and( 
				cb.equal( entity.join( "dict" ).get( "periodType" ), PeriodType.Season ),
				cb.or( 
						cb.lessThan( entity.get( "periodYear" ), cal.get( Calendar.YEAR ) - 1 ),
						cb.lessThan( entity.get( "periodSeqNo" ), cal.get( Calendar.MONTH ) / 3 )
				)
		);
		
		Predicate monthPeriodRestriction = cb.and( 
				cb.equal( entity.join( "dict" ).get( "periodType" ), PeriodType.Month ),
				cb.or( 
						cb.lessThan( entity.get( "periodYear" ), cal.get( Calendar.YEAR ) - 1 ),
						cb.lessThan( entity.get( "periodSeqNo" ), cal.get( Calendar.MONTH ) )
				)
		);
		
		restrictions.add( cb.or( yearPeriodRestriction, seasonPeriodRestriction, monthPeriodRestriction ) );
        
        criteria.where( restrictions.toArray(new Predicate[restrictions.size()]) );

		Session session = entityManager.unwrap( Session.class );
        org.hibernate.query.Query<AssessRecord> query = session.createQuery( criteria );
        ScrollableResults sr = query.setCacheMode( CacheMode.IGNORE ).scroll( ScrollMode.FORWARD_ONLY );
        
        long count = 0L;
		
		while (sr.next()) {
			AssessRecord assessRecord = (AssessRecord) sr.get( 0 );
			assessRecord.setRolledback( true );
			assessRecord.setRecordStatus( RecordStatus.Deleted );
			assessRecord.getServiceSupplier().setCreditScore( assessRecord.getServiceSupplier().getCreditScore().subtract( assessRecord.getScore() ) );
			
			session.update( assessRecord );
			session.update( assessRecord.getServiceSupplier() );
			
			count++;
			if (count % 200 == 0) {
				session.flush();
			}
		}
        
	}

	private static final String QUERY_ASSESS_RECORD_COUNT_EQL = ""
			+ "		SELECT		COUNT(ar.id) "
			+ "		FROM		AssessRecord ar "
			+ "		WHERE		ar.dict.id = :dictId "
			+ "		AND			ar.serviceSupplier.id = :supplierId "
			+ "		AND			ar.periodYear = :year "
			+ "		AND			ar.periodSn = :sn	";
	public boolean checkExist( ServiceSupplier supplier, Dict dict, Integer periodYear, Integer periodSn ) {
		Query query = entityManager.createQuery( QUERY_ASSESS_RECORD_COUNT_EQL );
		query.setParameter( "dictId", dict.getId() );
		query.setParameter( "supplierId", supplier.getId() );
		query.setParameter( "year", periodYear );
		query.setParameter( "sn", periodSn );
		return (Long) query.getSingleResult() > 0;
	}
	private final static String GET_ATTACHMENT_COUNT_BY_ASSESSRECORD_ID = ""
			+ " SELECT COUNT(9) "
			+ "	FROM   ASSESSRECORD_ATTACHMENT t		"
			+ "	WHERE t.ASSESSRECORD_ID = :assessRecordId";
	public BigDecimal getAttachmentCountById(String assessRecordId) {
		return (BigDecimal)entityManager.createNativeQuery(GET_ATTACHMENT_COUNT_BY_ASSESSRECORD_ID)
				.setParameter("assessRecordId", assessRecordId).getSingleResult();
	}
	
	@Transactional
	public File archive( String id ) {
		AssessRecord record = entityManager.find( AssessRecord.class, id );
				
		InputStream input = null;		
		try {
			File zipFile = File.createTempFile( "【".concat( record.getServiceSupplier().getCnname() ).concat( "_" ).concat( record.getDict().getTitle() ).concat( "】" ).concat( "上报材料" ), ".zip" );
			ZipOutputStream zipOut = new ZipOutputStream( new FileOutputStream( zipFile ) );
			
			for ( Attachment _a : record.getAttachments() ) {
				zipOut.putNextEntry( new ZipEntry( _a.getFilename() ) );		
				
				input = new FileInputStream( _a.getFile() );
				int temp = 0;
				while ( (temp = input.read()) != -1 ) {
					zipOut.write( temp );
				}
				input.close();
			}
			
			zipOut.close();
			
			return zipFile;
		} catch ( FileNotFoundException e ) {
			e.printStackTrace();
		} catch ( IOException e ) {
			e.printStackTrace();
		}
		
		return null;
	}
	
}
